home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Network Support Library
/
RoseWare - Network Support Library.iso
/
windows
/
bsdup2.exe
/
VIPX.DOC
< prev
Wrap
Text File
|
1993-10-19
|
33KB
|
942 lines
VIPX.386 Description, Limitations and Configuration Document
Current Version: 1.17
Date: 930819
Table of Contents
-----------------
I. Description
II. Limitations
A. Windows 3.X Versions
B. Version Compatibility with Dedicated IPX
C. Packet Size Limitations
D. Memory Manager Limitations
III. System Settings in the SYSTEM.INI File
A. [386Enh] Section
1. TimerCriticalSection
B. [VIPX] Section
1. VipxMappingPages
2. VipxFailOverSizedPackets
3. VpicdFix
4. AutoIrqVirtualize
5. VirtualizeIrq[0-F]
6. VipxErrorMessages
7. VipxWarningMessages
8. VipxOutDebugStrOnErrors
9. VipxOutDebugStrOnWarnings
10. VipxBreakOnErrors
11. VipxBreakOnWarnings
IV. Programming to the VIPX Interface
A. Getting the VIPX API Entry Point
B. Memory Management Issues
C. APIs Specific to VIPX.386
D. Handling Reentrancy Issues
E. Using NWIPXSPX.DLL
V. Global DOS TSRs using IPX/SPX
I. Description
---------------
VIPX.386 is a Windows 3.X virutalization driver for IPXODI.COM
driver. It virtualizes requests to the globally loaded IPX driver.
When a request is made to IPX, VIPX will allocate a request buffer in
the system's global memory, copy the original request to the global
buffer and give the global request to IPX. When the global request
completes, IPX will call VIPX. VIPX will then copy any results back
to the original request buffer and call the application which
submitted the request.
II. Limitations
----------------
II.A. Windows Versions
-----------------------
VIPX supports Windows versions 3.0, 3.0a and 3.1. However, there is
some functionality which VIPX will only support in Windows 3.1. This
is LAN IRQ virtualization for deadlock avoidence. Under WIndows 3.0
and 3.0a, VIPX will not virtualize LAN IRQs. See section III.B.5
VirtualizeIrq[0-F].
The preferred version of Windows is a patch of Windows 3.1.
Microsoft found a bug in the their Virtual Timer Device (VTD) driver
which would cause a deadlock with the NetWare shell (NETX). This
version of WIN386.EXE has a single byte patch which prevents the
situation from occurring.
The fix is in the Vitual Timer Device's control procedure. Without
the patch, a certain procedure is called during the Create_VM state.
With the patch, this procedure is now called during the
VM_Critical_Init state. The patch is applied at offset 441D8 in
WIN386.EXE (544789 03-01-92 3:10a). The value at that location is 07
(Create_VM). The value should be changed to 08 (VM_Critical_Init).
You may also address this bug by contacting Microsoft. They have created
a VTDA.386 driver. To obtain the driver from Microsoft, call their BBS
at 206-936-6735. The name of the file to download is WW0863.EXE.
The deadlock occurs as a result of the shell waiting for the timer
tick to advance. When an NCP is sent, the shell will start it's
retry timeout countdown. This is essentially a loop waiting for the
timer tick value to advance to a certain point. The deadlock occurs
when the tick value never increments and thus the shell is left in an
infinite loop. The reason the tick count is never incremented is
that the timer interrupt is not allowed to be simulated into V86 mode
under certain circumstances. The patch to the VTD allows the timer
interrupts to be simulated.
II.B. Version Compatibility with Dedicated IPX
-----------------------------------------------
Novell offically ceased maintenence on the dedicate IPX driver
(IPX.OBJ) verion 3.10 dated 11-21-91. The last version of VIPX to
explicitly support that driver is 1.10. The current version of VIPX
supports IPXODI.COM only. It is strongly recommended that you update
your dedicated IPX driver with IPXODI.COM when using versions of VIPX
later than 1.10.
II.C. Packet Size Limitations
------------------------------
VIPX.386 will only virtualize packets which are 8000 (decimal) bytes
or less. Any DOS and Windows IPX or SPX applications which use
networks with a physical packet size greater than 8000 bytes may not
work with VIPX.386. For example, IBM Token Ring can be configured to
run with 16K packets. A request by a DOS or Windows IPX application
to send a 16K packet will be truncated to 8000 bytes. On the other
hand, any 16K packet that is received by the LAN driver will be
dropped because VIPX cannot allocate a packet big enough to handle
it.
II.D. Memory Manager Limitations
---------------------------------
When an request is passed up from IPX, VIPX will immediately test the
request buffer to see if it is in global memory already. If it is in
global memory, the request will be passed back down to IPX without
any virtualization. For example, a TSR loaded before Windows is
considered to be in global memory. If that TSR calls IPX, VIPX will
test the requests and pass them back down to IPX. This is done
because there is no need to virtualize requests which are already
global.
The use of UMB memory complicates the test for global memory. There
are two basic senarios. Senario One is when a TSR has been loaded
high before Windows was loaded. In this case, IPX requests will come
from global UMB memory. VIPX will simply pass these requests back
down to IPX. Senario Two is when a TSR is loaded high in a Windows
DOSBOX. In this case, IPX requests will come from local UMB memory.
VIPX will virtualize these requests.
Using HIMEM.SYS and EMM386.SYS, all tests for global UMB memory will
work properly under both of senarios.
However, there are some memory managers that do not work properly
under Windows. With these drivers, all LOCAL DOSBOX UMBs look as if
they are GLOBAL UMBs. Under Senario Two, when a TSR calls IPX, VIPX
will test the request buffer and think that it is in global UMB
memory when it is really in LOCAL UMB memory. As a result, VIPX will
pass a local ECB to IPX without virtualization. The normal result of
this is a hung machine or data corruption.
This problem has only been reported with Compaq's EXMEM.SYS (version
unknown).
If you are using this memory manager, all TSRs USING THE IPX
INTERFACE (INCLUDING IPX ITSELF) MUST BE LOADED IN CONVENTIONAL
MEMORY.
III. System Settings in the SYSTEM.INI File
--------------------------------------------
III.A. [386Enh] Section
------------------------
III.A.1. TimerCriticalSection
------------------------------
As of version 1.15 of VIPX, TimerCriticalSection is required to be set
on. The recommended setting is as follows:
[386Enh]
TimerCriticalSection=10000
The reason for this parameter is to avoid a deadlock with the LAN IRQ
Virtualization code. See section III.B.5 VirtualizeIrq[0-F].
III.B. [VIPX] Section
------------------------
Under most circumstances, VIPX will work fine under the default
configuration. However, there may be some applications which require
custom configuration of the driver. This is a list of SYSTEM.INI
parameters which can be used to configure VIPX:
[VIPX]
VipxMappingPages =[number of 4K pages] (default = 16)
VipxFailOverSizedPackets =[ON|OFF|TRUE|FALSE] (default = OFF)
VpicdFix =[ON|OFF|TRUE|FALSE] (default = ON)
AutoIrqVirtualize =[ON|OFF|TRUE|FALSE] (default = ON)
VirtualizeIrq[0-F] =[ON|OFF|TRUE|FALSE] (default = OFF)
(BELOW ARE CONFIGURATION PARAMETERS FOR BETA VERSIONS ONLY!)
VipxErrorMessages =[ON|OFF|TRUE|FALSE] (default = OFF)
VipxWarningMessages =[ON|OFF|TRUE|FALSE] (default = OFF)
VipxBreakOnErrors =[ON|OFF|TRUE|FALSE] (default = OFF)
VipxBreakOnWarnings =[ON|OFF|TRUE|FALSE] (default = OFF)
VipxOutDebugStrOnErrors =[ON|OFF|TRUE|FALSE] (default = OFF)
VipxOutDebugStrOnWarnings =[ON|OFF|TRUE|FALSE] (default = OFF)
III.B.1. VipxMappingPages
--------------------------
This is the number of pages that VIPX can use to globalize requests
to the global IPXODI.COM driver. VIPX is not absolutely guaranteed
to have all of these pages available at any one point, since this is
the requested number of pages for SHARED global mapping which VIPX
makes to the Windows VMM at initialization time.
III.B.2. VipxFailOverSizedPackets
----------------------------------
This parameter tells VIPX to fail any requests which require more
than the maximum allowed globalization size. The actual maximum will
vary according to which media the user is on. The absolute maximum
is 8000 (decimal) bytes. With media that have smaller packets than
8000 bytes, the maximum allowed size is the maximum packet size that
can be put onto the media.
III.B.3. VpicdFix
------------------
III.B.4. AutoIrqVirtualize
---------------------------
VpicdFix and AutoIrqVirtualize are synonyms for the same option.
The VPICD VxD has a problem of allowing an interrupt to be simulated
to a VM which previously held the Windows Critical Section but currently
does not. (For more details, see III.B.5. VirtualizeIrq[0-F]). This
causes a deadlock situation when LAN I/O is attemped in another VM.
To get around the problem, VIPX will take over the job of virtualizing
LAN IRQs and will simulate LAN interrupts only to the owner of the
Windows Critical Section.
This option is used to turn on or off the automatic detection and
virtualization of LAN IRQs when ODI LAN drivers or dedicated LAN
drivers are loaded. The reason for this option is to allow OEMs to
virtualize their LAN drivers with their own VxDs.
III.B.5. VirtualizeIrq[0-F]
----------------------------
This is option is automatically configured excepted on machines using
the IBM LAN Support Program. This parameter tells VIPX to virtualize
the specified (hex) Interrupt ReQuest (IRQ) line. This feature is
mainly designed to assist the IBM LAN Support Program in avoiding the
"Black Screen of Death".
Because of a design flaw in the VPICD.386 driver, network interrupts
will sometimes deadlock a PC running Windows 3.X. VIPX 1.15 attempts
to avoid the deadlock by virtualizing the Network Interface Card's
(NIC) IRQ(s). With ODI and dedicated IPX (IPX.OBJ) drivers, VIPX
will automatically read the configuration of the NIC from the driver
and virtualize the selected IRQs.
The only time that VIPX cannot automatically detect the LAN IRQs is
when using the IBM LAN Support Program with either SLANSUP.OBJ or
LANSUP.COM. In this case, the LAN IRQ is not readable from the
driver. The reason (so I am told) is that IBM LAN Support does not
have an API to read the IRQ setting on the NIC: The only way to get
this information is to read the NIC hardware itself. The problem
with doing this is that the hardware can be Token Ring, PCN2 or
Ethernet. This means that VIPX would have to be aware of all of the
different configurations of all of these hardwares. Instead of this,
VIPX requires the IBM LAN Support user to specify the NIC IRQ in the
[VIPX] section of the SYSTEM.INI. IRQs range from 0 to F (hex). An
example is listed below:
[VIPX]
VirtualizeIrq2=TRUE
VirtualizeIrq3=TRUE
In this example, VIPX will virtualize both IRQ 2 and IRQ 3. VIPX can
virtualize upto 4 different LAN IRQs. The reason for virtualizing
multiple IRQs is to allow other LAN cards and protocols to be
installed on the same PC and prevent them from deadlocking the
machine. For example, you may have IPX running through an NE2000
card on IRQ 3 and TCP/IP running through to an IBM Token Ring card on
IRQ 2.
One side note. On an AT class PC, IRQ 2 is is really IRQ 9 on the
physical machine. IRQ 2 is a special cascade interrupt used to
access IRQs 9-F. Whenever a NIC raises the IRQ 2 line, the hardware
configuration on the PC's bus actually issues an IRQ 9. Also, if a
NIC is configured to IRQ 2, the LAN driver during its initialization
procedure will detect that the PC is an AT class machine and will
correctly configure its software to use IRQ 9.
Getting to the point, the Windows VPICD VxD will not allow IRQ 2 to
be exclusively virtualized by any VxD. However, when VIPX detects
that IRQ 2 is to be virtualized, it changes the IRQ to 9. So if you
are using the IBM LAN Support Program on an IBM Token Ring NIC set to
IRQ 2, you should have not trouble. The following two configurations
are equivalent:
[VIPX]
VirtualizeIrq2=TRUE
or
[VIPX]
VirtualizeIrq9=TRUE
One important note when using VIPX 1.15 for higher: Set the
TimerCriticalSection under the [386Enh] section in the SYSTEM.INI as
follows:
[386Enh]
TimerCriticalSection=10000
The reason for this is that the timer interrupt can cause a deadlock
with the virtualization of the the LAN interrupt.
You should note that the VirtualizeIrq[0-F] can be used to override
any auto-virtualization of LAN IRQs. For example, if your ODI LAN
driver is configured for IRQ 5, VIPX will automatically detect that
IRQ 5 is to be virtualized. However, if you have
[VIPX]
VirtualizeIrq5=FALSE
in your SYSTEM.INI, VIPX will override the automatic detection of IRQ
5 and not virtualize the interrupt.
Another way to disable the auto-virtualization feature of VIPX is
with the VpicdFix or AutoIrqVirtualize parameters (See III.B.3.
VpicdFix or III.B.4 AutoIrqVirtualize).
III.B.6. VipxErrorMessages
---------------------------
III.B.7. VipxWarningMessages
-----------------------------
III.B.8. VipxOutDebugStrOnErrors
---------------------------------
III.B.9. VipxOutDebugStrOnWarnings
-----------------------------------
III.B.10. VipxBreakOnErrors
---------------------------
III.B.11. VipxBreakOnWarnings
-----------------------------
These parameters are only enabled in Beta versions of VIPX. With Beta
software, the user can enable/disable these parameters to make VIPX
print error/warning messages to the user screen and debugger screen.
They can also instruct VIPX to execute a break point whenever an
error/warning occurs so that debugging can be done on site.
IV. Programming to the VIPX Interface
--------------------------------------
IV.A. Getting the VIPX API Entry Point
---------------------------------------
VIPX supports API calls from 16-bit DOS and Windows applications.
This is the interface which NWIPXSPX.DLL uses to issue calls to VIPX.
To get the VIPX Device V86/PM API Entry Point, a 16-Bit DOS/Windows
application must do the following:
DWORD VIPX_API_ADDR;
#define WIN_GET_DEV_API_ENTRY_PT 0x1684
#define VIPX_DEVICE_ID 0x0200
GetVipxApiAddr ()
{
_asm
{
mov ax, WIN_GET_DEV_API_ENTRY_PT
mov bx, VIPX_DEVICE_ID
int 2fh
mov word ptr VIPX_API_ADDR+2, ES
mov word ptr VIPX_API_ADDR, DI
}
if (!VIPX_API_ADDR)
{
print error - NO PM API! RIGHT VERS OF VIPX?
handle error
}
}
Once the API address is obtained, an application then can make a far
call to the VIPX API Handler to do two primary functions:
1. Request an 16-Bit PM IPX/SPX operation (SendPacket, etc...).
2. Request a device specific operation (GetVersion, GetDiagnotics).
The way that VIPX differentiates between the two types of APIs is by
the value in the BH register. If BH equals 00h, then VIPX will
handle a 16-Bit Protected Mode IPX/SPX operation. NOTE: The V86
IPX/SPX API interface (i.e. DOS application) is done through the
globally loaded IPX TSR itself. This way, DOS applications do not
need to change the way they access the IPX/SPX APIs under Windows and
DOS.
If BH equals the character 'V' (56h), then VIPX will will perform a
V86/PM Device Specific API. These APIs are listed below (and will be
presented in full specification later):
GetVipxVersion
GetVipxDiagnostics
For IPX/SPX operations (BH = 00h), a 16-bit Windows program must
follow the IPX/SPX API specification in the NetWare System Calls-DOS
manual under the Communications chapter. Coverage of the DOS IPX/SPX
APIs is beyond the scope of this document. However, an example is
provided below for reference:
DWORD VIPX_API_ADDR;
int ccode;
CallPmIpxSpxApi ()
{
_asm
{
mov bx, (IPX operation number)
(set other registers as specified by the function)
call VIPX_API_ADDR
(save return registers as specified by the function)
ErrorCondition = (function error condition)
}
if (ErrorCondition)
{
print error - function failed!
handle error
}
}
IV.B. Memory Management Issues
-------------------------------
One problem with using the IPX/SPX interface in Windows applications
is that all memory used by VIPX must be page-locked. Since
VIPX is an asynchronous driver, all application ECBs, fragments and
ESRs must be in locked memory to insure that they can be accessed
during interrupt time. If an ECB, fragment or ESR is in moveable
memory, it could move when VIPX "owns" that memory. For example,
assume an ECB is located at 16-bit PM address 0A10:0000 in moveable
memory. Assume this address tranlates to 32-bit linear address
8140ACB0. Assume also that the application gives the ECB to VIPX and
VIPX commences to work on it. Now if Windows does garbage
collection, the linear mapping for the 16-bit address 0A10:0000 may
change from 8140ABC0 to 81408100. When VIPX tries to access the old
linear address 8140ABC0, memory corruption will occur because VIPX is
accessing the wrong memory.
To avoid memory corruption, follow the guidelines below (your
comments on the effectiveness of these guidelines are appreciated):
1. Read Chapter 15 (Memory Management) and 16 (More Memory
Management) of the MS Windows SDK Version 3.1. These will explain
many terms and concepts which will be discussed below.
2. Define all ESRs, routines called by ESRs and data accessed by
ESRs in one module. In the .DEF file for your project, define the
module's code and data segments as FIXED.
3. Use GlobalPageLock() on all dynamically allocated heap memory
(via GlobalAlloc() or LocalAlloc()) which is to be used by VIPX or an
ESR. GlobalPageLock() informs the GUI kernel memory manager to fix
the linear base of the selector until a GlobalPageUnlock() is done on
the memory.
4. When using GlobalAlloc(), use the GMEM_DDESHARE and GMEM_FIXED
options. GMEM_DDESHARE will allocate the memory (so I am told) high
up on the global heap. This will avoid using conventional memory and
will allow room for other Windows applications to load after your
application has been loaded and initialized. The GMEM_FIXED option
tells the GUI kernel memory manager that the memory is FIXED. This
does not mean that the linear address will remain unchanged, but it
does mean that the segmented address (selector:offset) will not
change. After the call to GlobalAlloc(), call GlobalLock() to
derefernce the global memory handle. This provides a far pointer
(selector:offset) to the memory which will not change. Next call
GlobalPageLock() to fix the linear base of the selector so that the
32-bit linear mapping of the selector:offset does not change.
5. If local moveable or discardable code or data segments are used
for ECBs, fragments or ESRs, use GlobalPageLock() on those segments.
6. After you load your application and then you get a message from
Windows that there are not enough resources to load another program,
try changing the attributes of your segments. I believe this
condition occurs because too much conventional memory has been
allocated to fixed memory objects and Windows needs to allocate
conventional memory to load a program. One strategy may be to do a
GlobalCompact() with a argument of -1 at initialization time. This
will move all moveable segments and discard all discardable
segments. Next call GlobalAlloc() for one large moveable global
object which contains all of your ECBs, fragments and data which must
be accessed from VIPX or your ESRs. Next call GlobalLock() to
dereference the global handle. Last call GlobalPageLock() to fix the
linear address of the object. (I have not tried this yet, so there
may be some problems with it). Also make your ESR code as small as
possible using PostMessage to indicate to your application that an
ECB has completed. This way, your FIXED code segment will have
little impact on conventional memory.
7. Read Chapter 4 of The Windows Programmer's Guide to DLLs and
Memory Management by Mike Klein.
IV.C. APIs Specific to VIPX.386
--------------------------------
There are two APIs via the VIPX API entry point which are specific
to VIPX.386 only. These are:
GetVipxVersion
GetVipxDiagnostics
The device specific APIs are called in the same manner as the IPX/SPX
APIs except that BH = 56h. Some sample code is listed below:
DWORD VIPX_API_ADDR;
#define VIPX_DEVICE_API_TAG 'V'
int ccode;
CallVipxDeviceSpecificApi ()
{
_asm
{
mov bh, VIPX_DEVICE_API_TAG
mov bl, api_function_number
(set other registers for API function)
call VIPX_API_ADDR
mov ccode, ax
}
if (!ccode)
{
print error - api function failed!
handle error
}
}
The full API specification for GetVipxVersion is as follows:
GetVIPXVersion
This procedure puts the VIPX major and minor version numbers into
the client AH and AL.
To call this API a DOSBOX or Windows 3.0 application
must first get the VIPX Device API entry point (see
VIPX_V86_API_Handler or VIPX_PM_Handler). Then
it must do the following:
Example:
DWORD VIPX_API_ADDR;
BYTE VIPX_Major_Version;
BYTE VIPX_Minor_Version;
#define VIPX_DEVICE_API_TAG 'V'
#define VIPX_GET_VERSION 0x00
int ccode;
main ()
{
_asm
{
mov bh, VIPX_DEVICE_API_TAG
mov bl, VIPX_GET_VERSION
call VIPX_API_ADDR
mov ccode, ax
mov VIPX_Major_Version, bh
mov VIPX_Minor_Version, bl
}
if (!ccode)
{
print error - api function failed!
handle error
}
}
Entry:
BH 056h - VIPX Device API
BL 0 - Get VIPX Version function
Exit:
BH VIPX Major Version
BL VIPX Minor Version
AX Return code
0000h - success
Uses:
AX, BX, Flags
The full API specification for GetVIPXDiagnostics is as follows:
GetVIPXDiagnostics
This procedure puts the diagnostics for VIPX into the
specified client diagnostic buffer. The buffer is 112 bytes
and has the following structure:
VIPXDiagStruc STRUC
MallocCount dd ?
MallocErrorCount dd ?
FreeCount dd ?
AllocPageCount dd ?
FreePageCount dd ?
PMRequestCount dd ?
TSRServiceCount dd ?
ServiceAESEventCount dd ?
ServiceIPXEventCount dd ?
PostEventCount dd ?
IPXGetECBCount dd ?
IPXGetECBBadPacketCount dd ?
IPXGetECBOutOfResource dd ?
IPXCountECBCount dd ?
IPXReturnECBCount dd ?
IPXRequestCount dd ?
FreeECBCount dd ?
AllocECBCount dd ?
LockECBCount dd ?
UnlockECBCount dd ?
OpenSocketCount dd ?
CloseSocketCount dd ?
SendPacketCount dd ?
ListenCount dd ?
IPXScheduleEventCount dd ?
AESScheduleEventCount dd ?
CancelECBCount dd ?
PMInt2FCount dd ?
AllocPageErrorCount dd ?
FreePageErrorCount dd ?
FreeECBErrorCount dd ?
MaxVipxAllocSize dd ?
MaxVipxFragsSize dd ?
TsrServiceReenterCnt dd ?
IPXGetEcbSktNotOpenCnt dd ?
IPXGetEcbNoEcbAvailCnt dd ?
ENDS
NOTE: Starting with VIPX 1.16, MaxVipxAllocSize, MaxVipxFragsSize, and
TsrServiceReenterCnt have been updated to DWORDs. Also IPXGetEcbSktNotOpenCnt
and IPXGetEcbNoEcbAvailCnt have been added.
To call this API a DOSBOX or Windows 3.0 application
must first get the VIPX Device API entry point (see
VIPX_V86_API_Handler or VIPX_PM_Handler). Then
it must do the following:
Example:
DWORD VIPX_API_ADDR;
VIPXDiagStruc Diag;
#define VIPX_DEVICE_API_TAG 'V'
#define VIPX_GET_DIAGNOSTIC 0x01
int ccode;
main ()
{
_asm
{
mov bh, VIPX_DEVICE_API_TAG
mov bl, VIPX_GET_DIAGNOSTIC
mov ax,ds
mov es,ax
mov di, offset Diag
mov cx, size VIPXDiagStruc
call VIPX_API_ADDR
mov ccode, ax
}
if (!ccode)
{
print error - api function failed!
handle error
}
}
Entry:
BH 056h - VIPX Device API
BL 1 - Get VIPX Diagnostics
ES:SI ptr to client VIPX Diagnostics buffer.
CX Length of diagnostics buffer in bytes
Exit:
ES:SI client's buffer now has VIPX diags.
AX Return code
0000h - success
0FFFEh - diagnostics buffer too short
Uses:
AX, SI, ES, Flags
IV.D. Handling Reentrancy Issues
---------------------------------
A Windows application which uses the IPX/SPX APIs via VIPX must
handle reentrancy issues. Since the IPX/SPX API is interrupt driven,
any code running while interrupts are enabled can (and will) be
interrupted at any time by IPX/SPX. When an IPX event completes, an
ESR for that event is called. The ESR is entered with interrupts
disabled. If that ESR reenables interrupts, then it can be
reentered. This is usually a problem if:
1. The application has limited stack space
2. The application's ESR uses global variables
An ESR must protect itself from these two reentrancy issues if it
reenables interrupts. Interrupts can be reenabled by several
different places. One of the most notorious is the PostMessage()
Windows call. Any ESR using PostMessage() must be reentrant.
Another notorious enabler of interrupts is IPXSendPacket.
IPXSendPacket will call the LSL to transmit a packet. The LSL,
however, will reenable interrupts during transmission of a packet.
This can cause reentrancy into any ESR that calls IPXSendPacket.
An ESR can handle reentrancy issues by
1. Switching to a stack exclusively for the ESR to use.
2. Provide a reetrancy queue.
The stack switching is usually done in assembly. It is by far the
easiest and most efficient way to protect your stack. The code for
stack switching is listed below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
memL equ 1 ; large model
?PLM = 1 ; pascal conventions
?WIN = 1 ; epilogue/prolog code
INCLUDE cmacros.inc
sBegin DATA
assumes DS, DATA
ReenterCnt db 0
OldSs dw 0
OldSp dw 0
EsrStack db 4096 dup (0)
EndEsrStack label byte
sEnd DATA
sBegin CODE
assumes CS, CODE
;
; This is the front-end procedure to the C function "ReceiveESR"
;
externFP cESR
PUBLIC aESR
aESR proc FAR
nop
nop
nop
nop
; Don't know why Windows needs to inc bp
inc bp
push bp
mov bp, sp
; Save data segment
push ds
; Fix to our data segmented
mov ax, DGROUP
mov ds, ax
; Update our reenter cnt
inc ReenterCnt
; Do we need to switch stack to ESR stack?
cmp ReenterCnt, 2 ;are we reentering?
jae CallEsr ;y: call esr
mov ax, ss
cmp ax, DGROUP ;same stack seg?
jne SwitchStack ;n: switch
lea ax, EsrStack ;ax=offset of EsrStack
cmp sp, ax ;below the esr stack?
jb SwitchStack ;y: switch
lea ax, EndEsrStack ;ax=offset of end of EsrStack
cmp sp, ax ;above the esr stack
jae SwitchStack ;y: switch
; Call the esr
CallEsr:
push es
push si
cCall cESR
lea sp, [bp-2]
; Update reenter cnt
dec ReenterCnt
; Switch stacks back?
jz SwitchStackBack
; return to IPX
EsrExit:
; restore ds and bp
pop ds
pop bp
dec bp
ret
SwitchStack:
mov ax, sp
mov OldSp, ax
mov ax, ss
mov OldSs, ax
lea ax, EndEsrStack
mov sp, ax
mov ax, ds
mov ss, ax
jmp CallEsr
SwitchStackBack:
mov ax, OldSp
mov sp, ax
mov ax, OldSs
mov ss, ax
jmp EsrExit
aESR endp
sEnd CODE
end
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The above code switched to a 4K stack before calling the main ESR
code. Notice that there is a reentrancy count to determine if a stack
switch should occur. This is used to prevent switching the stack
again if the ESR is reentered.
The main ESR code is written in C. An example is listed below:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void far pascal cESR(ECB far *esr_ecb)
{
static ECB FAR * EcbQueue;
static ECB FAR * EcbQueueTail = (ECB FAR *)&EcbQueue;
static WORD ReenterFlag = 0;
// Check for reentrancy
if (ReenterFlag)
{
// Enqueue the ECB
EcbQueueTail->Link = (void far *)esr_ecb;
EcbQueueTail = (ECB far *)esr_ecb;
esr_ecb->Link = NULL;
return;
}
// Set the reentrany flag to indicate that we can't allow
// another thread through the ESR.
ReenterFlag++;
// Process the current ECB
NextEcb:
// Do some work here if need be -- BUT BE QUICK!
...
// Post a message to the Windows App that the event happened
PostMessage (
hMainWnd,
USER_DEFINED_IPX_EVENT_HAPPPENED,
0,
(DWORD) esr_ecb);
// Check for anything on the queue
CheckQueue:
if (EcbQueue)
{
esr_ecb = EcbQueue;
EcbQueue = (ECB FAR *)(EcbQueue->Link);
if (!EcbQueue)
EcbQueueTail = (ECB FAR *)&EcbQueue;
goto NextEcb;
}
// Reset the reentrancy flag to indicate that the
// first thread is done
ReenterFlag--;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
One side note about the code above. PostMessage() will queue a
message onto an applications message queue. This queue is limited in
size. If the application is fairly LAN I/O intensive, the message
queue can be overrun. In this case, PostMessage() will simply drop
the message. If this happens, your application will never get
notified of the event. To insure that messages are never dropped,
you may want to increase the size of your message queue. Another
alternative is to have the message indicate that an event happened
and the message loop will then poll all ECBs to determine which ones
completed. This way, if a message is dropped, it won't matter: the
main message loop will still detect that the ECB completed and will
be able to reuse the ECB.
IV.E. Using NWIPXSPX.DLL
-------------------------
Most Windows applications will use the IPX/SPX APIs via the
NWIPXSPX.DLL. The reason is that NWIPXSPX.DLL address the issues for
both Standard and Enhanced mode Windows. Under Standard mode,
NWIPXSPX will use TBMI2.COM to virtualize the local ECBs passed to
it. Under Enhanced mode, NWIPXSPX.DLL will use VIPX.386 to
virtualize ECBs. If you Windows application is targeted toward
Enhanced mode only, then you may find it more convenient to program
directly to the VIPX interface in assembly. However, if you are
interested in using an established C interface for IPX/SPX, then
NWIPXSPX.DLL is the way to go. There is example code using NWIPXSPX.DLL
on the Compuserve NOVDEV forum.
V. Global DOS TSRs using IPX/SPX
---------------------------------
TSRs loaded before Windows can call IPX without the intervention of
VIPX. This is done by ORing the BX register with 8000h (i.e. setting
the high-bit of the function number). This is a previously
undocumented feature in IPX to support the globally loaded NETX
driver. When IPX sees that a request has the high-bit set, it will
assume that the request came from a globally loaded TSR and will not
forward the request to VIPX.
The only problem with the high-bit is that SPX does not recognize
it. When SPX gets a request with a high-bit, SPX ignores it and
reposts the ECB to IPX without the preserving the high-bit. VIPX
will then get the IPX request and then determine if the ECB is really
loaded in global memory. If so, then it will simply pass the global
request back down to IPX with the high-bit set.